﻿using DongFangGuoKai.WCSPlatform.WebApi.ExternalSystem.WMSConmmon;
using DongFangGuoKai.WMSPlatform.Commn.ExtendModule;
using S7.Net;
using System.Linq;
using System.Text.RegularExpressions;

namespace DongFangGuoKai.WCSPlatform.WebApi.Utils
{
    /// <summary>
    /// Plc任务
    /// </summary>
    public partial class PlcTaskManager
    {
        private readonly Plc plc;
        private readonly PlcDBAddress plcDBAddress;
        private readonly WMSHttpClient wmsHttpClient;
        private readonly ILogger<PlcTaskManager> logger;



        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="plc"></param>
        /// <param name="plcDBAddress"></param>
        /// <param name="wmsHttpClient"></param>
        /// <param name="logger"></param>
        public PlcTaskManager
        (
            Plc plc,
            PlcDBAddress plcDBAddress,
            WMSHttpClient wmsHttpClient,
            ILogger<PlcTaskManager> logger
        )
        {
            this.plc = plc;
            this.plcDBAddress = plcDBAddress;
            this.wmsHttpClient = wmsHttpClient;
            this.logger = logger;
        }
        /// <summary>
        /// 箱子号规则
        /// </summary>
        static string pattern = @"^[A-Za-z]\d{5}$";
        /// <summary>
        /// 出库窗户口中的对应任务
        /// </summary>
        public static Dictionary<ushort, List<TaskPallet>> outWinCodeTaskPalletDic = new Dictionary<ushort, List<TaskPallet>>()
        {
            { 95,new List<TaskPallet>() },{ 94,new List<TaskPallet>()},
            { 93,new List<TaskPallet>() },{ 92,new List<TaskPallet>()},
            { 91,new List<TaskPallet>() },{ 90,new List<TaskPallet>()},
        };
        /// <summary>
        /// 获取全部出库任务
        /// </summary>
        /// <returns></returns>
        public Dictionary<ushort, List<TaskPallet>> GetOutTaskDic() => outWinCodeTaskPalletDic;
        /// <summary>
        /// 清除出库任务
        /// </summary>
        /// <param name="taskCode">为空则全部清除</param>
        /// <returns></returns>
        public bool DelOutTaskDic(ushort? taskCode)
        {
            try
            {
                if (taskCode == null)
                {
                    outWinCodeTaskPalletDic = new Dictionary<ushort, List<TaskPallet>>()
                    {
                        { 95,new List<TaskPallet>() },{ 94,new List<TaskPallet>()},
                        { 93,new List<TaskPallet>() },{ 92,new List<TaskPallet>()},
                        { 91,new List<TaskPallet>() },{ 90,new List<TaskPallet>()},
                    };
                    return true;
                }
                else
                {
                    foreach (var item in outWinCodeTaskPalletDic)
                    {
                        var task = item.Value.FirstOrDefault(s=>s.taskCode==taskCode);
                        if (task == null)
                        {
                            throw new Exception($"{taskCode}任务不存在");
                        }
                        item.Value.Remove(task);
                    }

                    return false;
                }

            }
            catch (Exception)
            {

                throw;
            }

        }

        /// <summary>
        /// 任务要做的事情
        /// </summary>
        /// <returns></returns>
        static string lastOutPalletCode = "\0\0\0\0\0\0";//上一个出库箱子号
        /// <summary>
        /// 出库分拨窗口
        /// </summary>
        public async void OutDoWork()
        {
            try
            {
                var palletList=new HashSet<string>();//现有的所有箱子号
                foreach (var item in outWinCodeTaskPalletDic)
                {
                    foreach (var citem in item.Value)
                    {
                        palletList.Add(citem.palletCode);
                    }
                }

                string? palletCode = plc.ReadString(plcDBAddress.DBNumber, plcDBAddress.OutPalletCode).HexToStr(); //箱子号
                if (palletCode != lastOutPalletCode 
                    && Regex.IsMatch(palletCode, pattern)
                    && palletList.Contains(palletCode)==false)//读到箱子号和上一次相同则说明没有读到箱子
                {
                    var wmsRes = wmsHttpClient.GetNumber(palletCode,out string httpLog);//请求WMS获得窗口号
                    if (wmsRes.Code == 500)
                    {
                        logger.LogError(httpLog+",WMS返回异常");
                        return;
                    }
                    if (wmsRes.Data.WinCode == 0)
                    {
                        logger.LogError("wms返回的窗口号为空");
                        return;
                    }
                    var dbAdress = DataTool.GetWinCodeDbAddress(wmsRes.Data.WinCode, plcDBAddress);//获取窗口号对应的任务地址
                    if (string.IsNullOrEmpty(dbAdress))//如果没有找到对应数据库地址说明WMS的窗口号错误
                    {
                        logger.LogError($"没有对应窗口号,WMS返回的窗口号{wmsRes.Data.WinCode}");
                        return;
                    }
                    lastOutPalletCode = palletCode;
                    var taskCode = DataTool.GetTaskCode();//生成一个任务号
                    await plc.WriteAsync(plcDBAddress.OutTaskCode, taskCode);//必须先写入任务号后写入目标站
                    await plc.WriteAsync(plcDBAddress.WinCode, wmsRes.Data.WinCode);//窗口号
                    if (outWinCodeTaskPalletDic.TryGetValue(wmsRes.Data.WinCode, out var taskPalletList))//绑定任务号和箱子号
                    {
                        taskPalletList.Add(new TaskPallet(taskCode, palletCode, wmsRes.Data.OrderSn,wmsRes.Data.OutBoundNo));
                    }
                    _ = Task.Run(async () =>
                    {
                        try
                        {
                            await ReadOutTask(dbAdress, wmsRes.Data.WinCode, taskCode);
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                    }); //一个箱子开启一个任务来跟踪 不需要使用await来等到任务完成,这样会阻塞代码 会读取不到下一个箱子
                    logger.LogInformation(httpLog+$",出库任务- taskCode:{taskCode},窗口号:{wmsRes.Data.WinCode},箱子号:{palletCode}");
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }
        /// <summary>
        /// 手动出库任务
        /// </summary>
        public async void ManualOutDoWork(string palletCode,ushort winCode)
        {
            try
            {
                if (palletCode != lastInPallCode)
                {
                    lastOutPalletCode = palletCode;
                    var dbAdress = DataTool.GetWinCodeDbAddress(winCode, plcDBAddress);//获取窗口号对应的任务地址
                    if (string.IsNullOrEmpty(dbAdress))//如果没有找到对应数据库地址说明WMS的窗口号错误
                    {
                        logger.LogError($"没有对应的窗口号");
                        return;
                    }
                    var wmsRes = wmsHttpClient.GetNumber(palletCode,out string httpLog);//请求WMS获得窗口号
                    if (wmsRes.Code == 500) return;
                    var taskCode = DataTool.GetTaskCode();//生成一个任务号
                    await plc.WriteAsync(plcDBAddress.OutTaskCode, taskCode);//写入任务号
                    await plc.WriteAsync(plcDBAddress.WinCode, (ushort)Convert.ToInt32(winCode));//窗口号
                    if (outWinCodeTaskPalletDic.TryGetValue(winCode, out var taskPalletList))//绑定任务号和箱子号和单号
                        taskPalletList.Add(new TaskPallet(taskCode, palletCode, wmsRes.Data.OrderSn, wmsRes.Data.OutBoundNo));
                    _ = Task.Run(async () =>
                    {
                        try
                        {
                            await ReadOutTask(dbAdress, winCode, taskCode);
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                    }); //一个任务开启一个线程去读取
                    logger.LogInformation(httpLog+$",手动出库任务- taskCode:{taskCode},窗口号:{winCode},箱子号:{palletCode}");
                }
                
            }
            catch (Exception ex)
            {
                throw;
            }
        }
        /// <summary>
        /// 开始读取相应的窗口号看看是否箱子被取走
        /// </summary>
        /// <param name="dbAddress">窗口地址</param>
        /// <param name="winCode">窗口号</param>
        /// <param name="taskCode">任务号</param>
        /// <returns></returns>
        public async Task ReadOutTask(string dbAddress, ushort winCode, ushort taskCode)
        {
            try
            {
                while (true)
                {
                    try
                    {
                        var dbVal = (ushort)plc.Read(dbAddress);//读取对应的窗口的值
                        if (plc.IsConnected && dbVal != 0 && dbVal == taskCode)//说明箱子已到达
                        {
                            var tryRes = outWinCodeTaskPalletDic.TryGetValue(winCode, out var taskPalletList);//读取对应窗口号中的所有任务
                            if (tryRes == false || taskPalletList == null || taskPalletList.Count() <= 0)
                            {
                                logger.LogError($"(ReadOutTask)WCS中窗口:{winCode}的任务列表为空");
                                continue;
                            }
                            var taskPallet = taskPalletList.FirstOrDefault(s => s.taskCode == dbVal);//找到已到达的箱子
                            if (taskPallet == null)
                            {
                                logger.LogError($"(ReadOutTask)在WCS中未找到任务{dbVal}");
                                continue;
                            }
                            //拿箱子号去找到对应的单号
                            var orderSnByPallet = taskPalletList.FirstOrDefault(s => s.palletCode == taskPallet.palletCode);
                            string customerHttpLog = string.Empty;
                            string outBoundHttpStr = string.Empty;
                            if (orderSnByPallet is not null)
                            {
                                try
                                {
                                    var customerRes = wmsHttpClient.NoticeOutCustomer(new NoticeOutCustomerReq()
                                    {
                                        OrderSn = orderSnByPallet.orderSn,
                                        BarCode = taskPallet.palletCode
                                    },out customerHttpLog);
                                    var boundRes = wmsHttpClient.NoticeOutBound(new NoticeOutBoundReq()
                                    {
                                        OrderSn = orderSnByPallet.orderSn,
                                        BarCode = taskPallet.palletCode,
                                        OutBoundNo = taskPallet.outBoundNo,
                                    },out outBoundHttpStr);
                                }
                                catch (Exception ex)
                                {
                                    logger.LogError($"(ReadOutTask)通知WMS箱子到位接口异常:{ex.Message}");
                                }
                            }
                            else
                                logger.LogError($"(ReadOutTask)在出库任务中未找到箱子号{taskPallet.palletCode}的数据");

                            string logStr = $"窗口{winCode}的箱子{taskPallet.palletCode}已到位";
                            taskPalletList.Remove(taskPallet);
                            logger.LogInformation(customerHttpLog + outBoundHttpStr + logStr + $",窗口{winCode}剩余任务列表：" + taskPalletList.ToJson());

                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.LogError($"出库任务异常(ReadOutTask):{ex.Message}");
                    }
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            }
            catch (Exception)
            {
                throw;
            }
            
        }



        /// <summary>
        /// 入库任务 key：TaskCode  val:托盘条码
        /// </summary>
        public static Dictionary<ushort,string> inTaskPalletDic=new Dictionary<ushort, string>();
        /// <summary>
        /// 获取全部入库任务
        /// </summary>
        /// <returns></returns>
        public Dictionary<ushort, string> GetInTaskDic() => inTaskPalletDic;
        /// <summary>
        /// 清除入库任务
        /// </summary>
        /// <param name="taskCode">为空则全部清除</param>
        /// <returns></returns>
        public bool DelInTaskDic(ushort? taskCode)
        {
            try
            {
                if (taskCode == null)
                {
                    inTaskPalletDic = new Dictionary<ushort, string>();
                    logger.LogInformation("清除了全部入库任务");
                    return true;
                }
                else
                {
                    if (inTaskPalletDic.TryGetValue(taskCode.Value, out var palletCode))
                    {
                        inTaskPalletDic.Remove(taskCode.Value);
                    }
                    else throw new Exception($"{taskCode}任务不存在");
                    if (inTaskPalletDic.ContainsKey(taskCode.Value) == false)
                    {
                        logger.LogInformation($"任务:{taskCode.Value},已被清除");
                        return true;
                    }
                        
                    return false;
                }
           
            }
            catch (Exception)
            {

                throw;
            }
          
        }
        /// <summary>
        /// 上一个入库箱子号
        /// </summary>
        static string lastInPallCode = "\0\0\0\0\0\0";
        /// <summary>
        /// 入库任务
        /// </summary>
        public async void InDoWork()
        {
            try
            {
                string? palletCode = plc.ReadString(plcDBAddress.DBNumber, plcDBAddress.InPalletCode).HexToStr(); //入库箱子号
                if (palletCode != lastInPallCode
                    && Regex.IsMatch(palletCode, pattern)
                    && inTaskPalletDic.Values.Contains(palletCode)==false)
                {
                    lastInPallCode = palletCode;
                    var taskCode = DataTool.GetTaskCode();
                    await plc.WriteAsync(plcDBAddress.InTaskCode, taskCode); //必须先写入任务号后写入目标站
                    await plc.WriteAsync(plcDBAddress.InTaskEnd, (ushort)20);//入库目标站
                    inTaskPalletDic.Add(taskCode, palletCode);
                    logger.LogInformation($"入库任务- taskCode:{taskCode},箱子号:{palletCode}");
                    _ = Task.Run(async () =>
                    {
                        try
                        {
                            await ReadInTask(taskCode);
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                    });//一个箱子开启一个任务来跟踪 不需要使用await来等到任务完成,这样会阻塞代码 会读取不到下一个箱子
                }
            }
            catch (Exception)
            {

                throw;
            }
          
        }
        /// <summary>
        /// 手动下任务测试使用
        /// </summary>
        public async void ManualInDoWork(string palletCode)
        {
            try
            {
                if (palletCode != lastInPallCode)
                {
                    lastInPallCode=palletCode;
                    var taskCode = DataTool.GetTaskCode();
                    await plc.WriteAsync(plcDBAddress.InTaskCode, taskCode); //必须先写入任务号后写入目标站
                    await plc.WriteAsync(plcDBAddress.InTaskEnd, (ushort)20);//入库目标站
                    inTaskPalletDic.Add(taskCode, palletCode);
                    logger.LogInformation("手动入库任务,任务号:"+taskCode);
                    _ = Task.Run(async () =>
                    {
                        try
                        {
                            await ReadInTask(taskCode);
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                    });//一个箱子开启一个任务来跟踪 不需要使用await来等到任务完成,这样会阻塞代码 会读取不到下一个箱子
                }
                
            }
            catch (Exception)
            {

                throw;
            }

        }
        /// <summary>
        /// 读入库任务
        /// </summary>
        /// <param name="taskCode">入库任务号</param>
        /// <returns></returns>
        public async Task ReadInTask(ushort taskCode)
        {
            try
            {
                while (true)
                {
                    try
                    {
                        var inTaskCodeVal = (ushort)plc.Read(plcDBAddress.InTerminalTask);
                        if (inTaskCodeVal == taskCode)
                        {
                            if (inTaskPalletDic.TryGetValue(inTaskCodeVal, out var palletCode))
                            {
                                var wmsRes = wmsHttpClient.NoticeInBox(palletCode,out var httpLog);
                                if (wmsRes.Code is 500) logger.LogError(httpLog+$",WMS返回异常,请手动处理箱子{palletCode}");
                                inTaskPalletDic.Remove(inTaskCodeVal);//清除已完成的任务
                                logger.LogInformation(httpLog+$",已通知数字孪生和WMS,入库箱子号{palletCode}已到位,剩余入库任务列表:{inTaskPalletDic.ToJson()}");

                                break;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.LogError($"入库任务异常(ReadInTask):{ex.Message}");
                    }
                    
                    await Task.Delay(1000);
                }
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 在开机前检查有没有未结束的任务 
        /// </summary>
        public void CheckTask()
        {
            foreach (var item in outWinCodeTaskPalletDic)
            {
                if (item.Value.Count > 0)
                {
                    foreach (var citem in item.Value)
                    {
                        Task.Run(async () =>
                        {
                            try
                            {
                                await ReadOutTask(DataTool.GetWinCodeDbAddress(item.Key, plcDBAddress), item.Key, citem.taskCode);
                            }
                            catch (Exception ex)
                            {
                                throw;
                            }
                        });
                    }
                }
            }

            foreach (var item in inTaskPalletDic)
            {
                Task.Run(async () =>
                {
                    try
                    {
                        await ReadInTask(item.Key);
                    }
                    catch (Exception ex)
                    {
                        throw;
                    }
                });
            }
        }
    }
}
